using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using PokerLearner;
using PokerLearner.Poker;

namespace Client
{
	public partial class MainWindow : Form
	{
		/// <summary>
		/// Delegate used for threadsafe AddPlayer
		/// </summary>
		/// <param name="client"></param>
		private delegate void Callback();
		private delegate void PlayerCallback(Player player);
		private delegate void RoundCallback(Round round);
		private delegate void PlayCallback(Play play);
		private delegate void PlayListCallback(List<Play> plays);
		private delegate void EventArgsCallback(EventArgs args);

		public MainWindow()
		{
			InitializeComponent();
			PokerClient.Instance.Connected += new EventHandler(PokerClient_Connected);
			PokerClient.Instance.Disconnected += new EventHandler(PokerClient_Disconnected);
			PokerClient.Instance.NewRound += new EventHandler(PokerClient_NewRound);
			PokerClient.Instance.PlayerAdded += new EventHandler<PlayerEventArgs>(PokerClient_PlayerAdded);
			PokerClient.Instance.PlayerRemoved += new EventHandler<PlayerEventArgs>(PokerClient_PlayerRemoved);
			PokerClient.Instance.PlayRequested += new EventHandler<PlayListEventArgs>(PokerClient_PlayRequested);
			PokerClient.Instance.CardUpdate += new EventHandler(PokerClient_CardUpdate);
			PokerClient.Instance.RoundEnded += new EventHandler<EndRoundEventArgs>(PokerClient_RoundEnded);
		}

		#region Player List Control

		


		/// <summary>
		/// Thread safe add player
		/// </summary>
		/// <param name="player"></param>
		private void AddPlayer(Player player)
		{
			if (listViewPlayers.InvokeRequired)
			{
				this.Invoke(new PlayerCallback(AddPlayer), new object[] { player });
			}
			else
			{
				string[] columns = { player.Name, player.Stack.ToString(), "", "" };
				ListViewItem item = new ListViewItem(columns);
				item.Tag = player;
				player.StackChanged += new EventHandler<PlayerEventArgs>(Player_StackChanged);
				listViewPlayers.Items.Add(item);
			}
		}

		

		/// <summary>
		/// Thread safe remove player
		/// </summary>
		/// <param name="player"></param>
		private void RemovePlayer(Player player)
		{
			if (listViewPlayers.InvokeRequired)
			{
				this.Invoke(new PlayerCallback(RemovePlayer), new object[] { player });
			}
			else
			{
				for (int i = 0; i < listViewPlayers.Items.Count; i++)
				{
					ListViewItem item = listViewPlayers.Items[i];
					if (player.Equals(item.Tag))
					{
						listViewPlayers.Items.RemoveAt(i);
						break;
					}
				}
			}
		}

		#endregion

		#region Player List Update Methods
		
		private void UpdatePlayerRoundInfo(Round round)
		{
			if (listViewPlayers.InvokeRequired)
				this.Invoke(new RoundCallback(UpdatePlayerRoundInfo), new object[] { round });
			else
			{
				foreach (ListViewItem item in listViewPlayers.Items)
				{
					if (!PokerClient.Instance.Round.Players.Contains((Player)item.Tag))
					{
						item.SubItems[3].Text = "Sitting Out";
						item.ForeColor = Color.Gray;
					}
					else
					{
						item.ForeColor = Color.Black;
					}

					if (PokerClient.Instance.Round.Dealer.Equals(item.Tag))
						item.SubItems[2].Text = "D";
					else
						item.SubItems[2].Text = " ";
				}
			}
		}

		private void UpdatePlayerPlayInfo(Play play)
		{
			if (listViewPlayers.InvokeRequired)
				this.Invoke(new PlayCallback(UpdatePlayerPlayInfo), new object[] { play });
			else
			{
				foreach (ListViewItem item in listViewPlayers.Items)
				{
					if (play.Player.Equals(item.Tag))
						item.SubItems[3].Text = play.ToString();
				}

				textBoxPot.Text = PokerClient.Instance.Round.Pot.ToString();
			}
		}

		private void UpdateCurrentPlayer(Player player)
		{
			if (listViewPlayers.InvokeRequired)
				this.Invoke(new PlayerCallback(UpdateCurrentPlayer), new object[] { player });
			else
			{
				foreach (ListViewItem item in listViewPlayers.Items)
				{
					if (player.Equals(item.Tag))
						item.ForeColor = Color.Blue;
					else
						item.ForeColor = Color.Black;
				}
			}
		}

		private void UpdatePlayerStack(Player player)
		{
			if (listViewPlayers.InvokeRequired)
			{
				this.Invoke(new PlayerCallback(UpdatePlayerStack), new object[] { player });
			}
			else
			{
				foreach (ListViewItem item in listViewPlayers.Items)
				{
					if (player.Equals(item.Tag))
					{
						item.SubItems[1].Text = player.Stack.ToString();
					}
				}
			}
		}

		private void ClearPlayerStatus()
		{
			if (listViewPlayers.InvokeRequired)
			{
				this.Invoke(new Callback(ClearPlayerStatus));
			}
			else
			{
				foreach (ListViewItem item in listViewPlayers.Items)
				{
					item.SubItems[3].Text = "";
				}
			}
		}

		private void ClearActivePlayerStatus()
		{
			if (listViewPlayers.InvokeRequired)
				this.Invoke(new Callback(ClearActivePlayerStatus));
			else
			{
				foreach (ListViewItem item in listViewPlayers.Items)
				{
					if (PokerClient.Instance.Round.Players.Contains((Player)item.Tag))
						item.SubItems[3].Text = "";
				}
			}
		}

		private void SetEndOfRoundStatus(EventArgs args)
		{
			EndRoundEventArgs eorArgs = (EndRoundEventArgs)args;
			if (listViewPlayers.InvokeRequired)
				this.Invoke(new EventArgsCallback(SetEndOfRoundStatus), new object[] { args });
			else
			{
				foreach (ListViewItem item in listViewPlayers.Items)
				{
					if (eorArgs.Winners.Contains((Player)item.Tag))
						item.SubItems[3].Text = String.Format("Won {0} with {1}", eorArgs.PlayerPayoff[(Player)item.Tag], eorArgs.PlayerHands[(Player)item.Tag].ToString(true));
				}

				textBoxPot.Text = "";
			}
		}

		#endregion

		#region Action Button Update Methods

		public void UpdateActionButtons(List<Play> plays)
		{
			if (listViewPlayers.InvokeRequired)
			{
				this.Invoke(new PlayListCallback(UpdateActionButtons), new object[] { plays });
			}
			else
			{
				numericUpDownRaise.Value = 1;
				foreach (Play play in plays)
				{
					if (play.Type == PlayType.Check)
					{
						buttonCheck.Enabled = true;
						buttonCheck.Tag = play;
					}
					else if (play.Type == PlayType.Call)
					{
						buttonCall.Enabled = true;
						buttonCall.Tag = play;
					}
					else if (play.Type == PlayType.Bet)
					{
						buttonBet.Enabled = true;
						buttonBet.Tag = play;
						numericUpDownRaise.Enabled = true;
					}
					else if (play.Type == PlayType.Raise)
					{
						buttonRaise.Enabled = true;
						buttonRaise.Tag = play;
						numericUpDownRaise.Enabled = true;
					}
					else if (play.Type == PlayType.AllIn)
					{
						buttonAllIn.Enabled = true;
						buttonAllIn.Tag = play;
					}
					else if (play.Type == PlayType.Fold)
					{
						buttonFold.Enabled = true;
						buttonFold.Tag = play;
					}
				}
			}
		}

		private void DisableActionButtons()
		{
			if (listViewPlayers.InvokeRequired)
				this.Invoke(new Callback(DisableActionButtons));
			else
			{
				buttonCheck.Enabled = false;
				buttonCheck.Tag = null;
				buttonCall.Enabled = false;
				buttonCall.Tag = null;
				buttonBet.Enabled = false;
				buttonBet.Tag = null;
				buttonRaise.Enabled = false;
				buttonRaise.Tag = null;
				numericUpDownRaise.Enabled = false;
				buttonAllIn.Enabled = false;
				buttonAllIn.Tag = null;
				buttonFold.Enabled = false;
				buttonFold.Tag = null;
			}
		}

		#endregion

		#region Card Control

		private void UpdateCards()
		{
			if (PokerClient.Instance.Round.State == RoundState.PreFlop)
			{
				pictureBoxCard1.Image = imageListDeck.Images[PokerClient.Instance.Pocket[0].Value];
				pictureBoxCard2.Image = imageListDeck.Images[PokerClient.Instance.Pocket[1].Value];
			}
			else if (PokerClient.Instance.Round.State == RoundState.Flop)
			{
				pictureBoxCommonCard1.Image = imageListDeck.Images[PokerClient.Instance.Round.CommonCards[0].Value];
				pictureBoxCommonCard2.Image = imageListDeck.Images[PokerClient.Instance.Round.CommonCards[1].Value];
				pictureBoxCommonCard3.Image = imageListDeck.Images[PokerClient.Instance.Round.CommonCards[2].Value];
			}
			else if (PokerClient.Instance.Round.State == RoundState.Turn)
			{
				pictureBoxCommonCard4.Image = imageListDeck.Images[PokerClient.Instance.Round.CommonCards[3].Value];
			}
			else if (PokerClient.Instance.Round.State == RoundState.River)
			{
				pictureBoxCommonCard5.Image = imageListDeck.Images[PokerClient.Instance.Round.CommonCards[4].Value];
			}
		}

		private void UpdateHand()
		{
			if (labelHand.InvokeRequired)
				this.Invoke(new Callback(UpdateHand));
			else
			{				
				labelHand.Text = (PokerClient.Instance.Hand != null) ? PokerClient.Instance.Hand.ToString(false) : "No Hand";
			}
		}

		private void UpdateMenuDisconnected()
		{
			if (listViewPlayers.InvokeRequired)
				this.Invoke(new Callback(UpdateMenuDisconnected));
			else
			{
				connectToolStripMenuItem.Enabled = true;
				disconnectToolStripMenuItem.Enabled = false;

				listViewPlayers.Items.Clear();
			}
		}

		#endregion

		#region Player Event Handlers

		void Player_StackChanged(object sender, PlayerEventArgs args)
		{
			UpdatePlayerStack(args.Player);
		}

		#endregion

		#region Round Event Handlers

		private void Round_NewPlay(object sender, PlayEventArgs args)
		{
			UpdatePlayerPlayInfo(args.Play);
		}

		private void Round_CurrentPlayerChanged(object sender, PlayerEventArgs args)
		{
			UpdateCurrentPlayer(args.Player);
		}

		private void Round_StateChanged(object sender, EventArgs e)
		{
			if (PokerClient.Instance.Round.State != RoundState.BigBlind || PokerClient.Instance.Round.State != RoundState.PreFlop)
			{
				ClearActivePlayerStatus();
			}
		}

		#endregion

		#region PokerClient Event Handlers

		void PokerClient_Connected(object sender, EventArgs e)
		{
			connectToolStripMenuItem.Enabled = false;
			disconnectToolStripMenuItem.Enabled = true;
		}

		void PokerClient_Disconnected(object sender, EventArgs e)
		{
			UpdateMenuDisconnected();
		}

		void PokerClient_NewRound(object sender, EventArgs e)
		{
			UpdatePlayerRoundInfo(PokerClient.Instance.Round);
			
			pictureBoxCard1.Image = null;
			pictureBoxCard2.Image = null;
			pictureBoxCommonCard1.Image = null;
			pictureBoxCommonCard2.Image = null;
			pictureBoxCommonCard3.Image = null;
			pictureBoxCommonCard4.Image = null;
			pictureBoxCommonCard5.Image = null;

			PokerClient.Instance.Round.NewPlay += new EventHandler<PlayEventArgs>(Round_NewPlay);
			PokerClient.Instance.Round.CurrentPlayerChanged += new EventHandler<PlayerEventArgs>(Round_CurrentPlayerChanged);
			PokerClient.Instance.Round.StateChanged += new EventHandler(Round_StateChanged);

			UpdateHand();
		}

		void PokerClient_PlayerAdded(object sender, PlayerEventArgs args)
		{
			AddPlayer(args.Player);
		}

		void PokerClient_PlayerRemoved(object sender, PlayerEventArgs args)
		{
			RemovePlayer(args.Player);
		}

		void PokerClient_PlayRequested(object sender, PlayListEventArgs args)
		{
			UpdateActionButtons(args.Plays);
		}

		void PokerClient_CardUpdate(object sender, EventArgs e)
		{
			UpdateCards();
			UpdateHand();
		}

		void PokerClient_RoundEnded(object sender, EndRoundEventArgs args)
		{
			SetEndOfRoundStatus(args);
		}

		#endregion

		#region Control Event Handlers

		private void connectToolStripMenuItem_Click(object sender, EventArgs e)
		{
			FormConnect dialog = new FormConnect();
			dialog.ShowDialog();
		}

		private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
		{
			PokerClient.Instance.Disconnect();
		}
		
		private void buttonCheck_Click(object sender, EventArgs e)
		{
			PokerClient.Instance.MakePlay((Play)buttonCheck.Tag);
			DisableActionButtons();
		}

		private void buttonCall_Click(object sender, EventArgs e)
		{
			PokerClient.Instance.MakePlay((Play)buttonCall.Tag);
			DisableActionButtons();
		}

		private void buttonBet_Click(object sender, EventArgs e)
		{
			Play play = (Play)buttonBet.Tag;
			play.Value *= (int)numericUpDownRaise.Value;
			PokerClient.Instance.MakePlay(play);
			DisableActionButtons();
		}

		private void buttonRaise_Click(object sender, EventArgs e)
		{
			Play play = (Play)buttonRaise.Tag;
			play.Value += PokerClient.Instance.Round.BigBlind * (int)numericUpDownRaise.Value;
			PokerClient.Instance.MakePlay(play);
			DisableActionButtons();
		}

		private void buttonAllIn_Click(object sender, EventArgs e)
		{
			PokerClient.Instance.MakePlay((Play)buttonAllIn.Tag);
			DisableActionButtons();
		}

		private void buttonFold_Click(object sender, EventArgs e)
		{
			PokerClient.Instance.MakePlay((Play)buttonFold.Tag);
			DisableActionButtons();
		}

		private void MainWindow_FormClosed(object sender, FormClosedEventArgs e)
		{
			PokerClient.Instance.Disconnect();
		}

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PokerClient.Instance.Disconnect();
            this.Close();
		}

		private void numericUpDownRaise_ValueChanged(object sender, EventArgs e)
		{
			if ((numericUpDownRaise.Value + 1) * PokerClient.Instance.Round.BigBlind > PokerClient.Instance.Player.Stack)
				numericUpDownRaise.Value--;
		}

		#endregion

		
	}
}